<!-- build time:Tue Feb 18 2020 17:19:27 GMT+0800 (China Standard Time) --><!DOCTYPE html><html lang="zh"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no,minimal-ui"><meta name="renderer" content="webkit"><meta http-equiv="Cache-Control" content="no-transform"><meta http-equiv="Cache-Control" content="no-siteapp"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black"><meta name="format-detection" content="telephone=no,email=no,adress=no"><meta name="theme-color" content="#000000"><meta http-equiv="window-target" content="_top"><title>Spring Boot JPA 入门 | calm&#39;s blog</title><meta name="description" content="摘要: 原创出处 http:&#x2F;&#x2F;www.iocoder.cn&#x2F;Spring-Boot&#x2F;JPA&#x2F; 「芋道源码」，卡姆在此基础上略作修改，欢迎转载，保留摘要，谢谢！1. 概述我们，咱们来学习下 Spring Data JPA 。相信不少胖友之前有了解过 JPA、Hibernate ，那么 JPA、Hibernate、Spring Data JPA 这三者是什么关系呢？我们来一起理一理。JPA ，全称"><meta property="og:type" content="article"><meta property="og:title" content="Spring Boot JPA 入门"><meta property="og:url" content="https://calm_java.gitee.io/blog/2020/01/08/labs-springboot2-jpa/index.html"><meta property="og:site_name" content="卡姆"><meta property="og:description" content="摘要: 原创出处 http:&#x2F;&#x2F;www.iocoder.cn&#x2F;Spring-Boot&#x2F;JPA&#x2F; 「芋道源码」，卡姆在此基础上略作修改，欢迎转载，保留摘要，谢谢！1. 概述我们，咱们来学习下 Spring Data JPA 。相信不少胖友之前有了解过 JPA、Hibernate ，那么 JPA、Hibernate、Spring Data JPA 这三者是什么关系呢？我们来一起理一理。JPA ，全称"><meta property="og:locale" content="zh_CN"><meta property="og:image" content="https://calm_java.gitee.io/blog/2020/01/08/labs-springboot2-jpa/1.png"><meta property="article:published_time" content="2020-01-08T09:57:01.000Z"><meta property="article:modified_time" content="2020-01-08T10:50:12.890Z"><meta property="article:author" content="calm"><meta property="article:tag" content="jpa"><meta name="twitter:card" content="summary"><meta name="twitter:image" content="https://calm_java.gitee.io/blog/2020/01/08/labs-springboot2-jpa/1.png"><link rel="canonical" href="https://calm_java.gitee.io/blog/2020/01/08/labs-springboot2-jpa/index.html"><link rel="alternate" href="/atom.xml" title="卡姆" type="application/atom+xml"><link rel="icon" href="/blog/images/avatar.jpg" type="image/x-icon"><link rel="stylesheet" href="/blog/css/style.css"><link rel="stylesheet" href="//cdn.jsdelivr.net/npm/gitalk@1.4.0/dist/gitalk.min.css"><meta name="generator" content="Hexo 4.2.0"></head><body class="main-center theme-green" itemscope itemtype="http://schema.org/WebPage"><header class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="slimContent"><div class="navbar-header"><div class="profile-block text-center"><a id="avatar" href="https://github.com/yuyong725" target="_blank"><img class="img-circle img-rotate" src="/blog/images/avatar.jpg" width="200" height="200"></a><h2 id="name" class="hidden-xs hidden-sm">卡姆</h2><h3 id="title" class="hidden-xs hidden-sm hidden-md">a Java coder need calm</h3><small id="location" class="text-muted hidden-xs hidden-sm"><i class="icon icon-map-marker"></i> hangzhou, China</small></div><div class="search" id="search-form-wrap"><form class="search-form sidebar-form"><div class="input-group"><input type="text" class="search-form-input form-control" placeholder="搜索"> <span class="input-group-btn"><button type="submit" class="search-form-submit btn btn-flat" onclick="return!1"><i class="icon icon-search"></i></button></span></div></form><div class="ins-search"><div class="ins-search-mask"></div><div class="ins-search-container"><div class="ins-input-wrapper"><input type="text" class="ins-search-input" placeholder="想要查找什么..." x-webkit-speech> <button type="button" class="close ins-close ins-selectable" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button></div><div class="ins-section-wrapper"><div class="ins-section-container"></div></div></div></div></div><button class="navbar-toggle collapsed" type="button" data-toggle="collapse" data-target="#main-navbar" aria-controls="main-navbar" aria-expanded="false"><span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span></button></div><nav id="main-navbar" class="collapse navbar-collapse" itemscope itemtype="http://schema.org/SiteNavigationElement" role="navigation"><ul class="nav navbar-nav main-nav"><li class="menu-item menu-item-home"><a href="/blog/."><i class="icon icon-home-fill"></i> <span class="menu-title">首页</span></a></li><li class="menu-item menu-item-archives"><a href="/blog/archives"><i class="icon icon-archives-fill"></i> <span class="menu-title">归档</span></a></li><li class="menu-item menu-item-categories"><a href="/blog/categories"><i class="icon icon-folder"></i> <span class="menu-title">分类</span></a></li><li class="menu-item menu-item-tags"><a href="/blog/tags"><i class="icon icon-tags"></i> <span class="menu-title">标签</span></a></li><li class="menu-item menu-item-repository"><a href="/blog/repository"><i class="icon icon-project"></i> <span class="menu-title">项目</span></a></li><li class="menu-item menu-item-books"><a href="/blog/books"><i class="icon icon-book-fill"></i> <span class="menu-title">书单</span></a></li><li class="menu-item menu-item-links"><a href="/blog/links"><i class="icon icon-friendship"></i> <span class="menu-title">友链</span></a></li><li class="menu-item menu-item-about"><a href="/blog/about"><i class="icon icon-cup-fill"></i> <span class="menu-title">关于</span></a></li></ul><ul class="social-links"><li><a href="https://github.com/yuyong725" target="_blank" title="Github" data-toggle="tooltip" data-placement="top"><i class="icon icon-github"></i></a></li></ul></nav></div></header><aside class="sidebar" itemscope itemtype="http://schema.org/WPSideBar"><div class="slimContent"><div class="widget"><h3 class="widget-title">公告</h3><div class="widget-body"><div id="board"><div class="content"><p>欢迎交流与分享经验!</p></div></div></div></div><div class="widget"><h3 class="widget-title">分类</h3><div class="widget-body"><ul class="category-list"><li class="category-list-item"><a class="category-list-link" href="/blog/categories/CI-DI/">CI/DI</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/blog/categories/git/">git</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/blog/categories/labs/">labs</a><span class="category-list-count">14</span></li></ul></div></div><div class="widget"><h3 class="widget-title">标签</h3><div class="widget-body"><ul class="tag-list" itemprop="keywords"><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/MyBatis/" rel="tag">MyBatis</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/Oauth2/" rel="tag">Oauth2</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/Spring-Security/" rel="tag">Spring Security</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/SpringMVC/" rel="tag">SpringMVC</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/docker/" rel="tag">docker</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/dubbo/" rel="tag">dubbo</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/git/" rel="tag">git</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/jdbctemplate/" rel="tag">jdbctemplate</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/jerkins/" rel="tag">jerkins</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/job/" rel="tag">job</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/jpa/" rel="tag">jpa</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/pdf/" rel="tag">pdf</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/redis/" rel="tag">redis</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/%E5%88%86%E5%BA%93%E5%88%86%E8%A1%A8/" rel="tag">分库分表</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/%E5%A4%9A%E6%95%B0%E6%8D%AE%E6%BA%90/" rel="tag">多数据源</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/%E6%95%B0%E6%8D%AE%E5%BA%93%E7%89%88%E6%9C%AC%E7%AE%A1%E7%90%86/" rel="tag">数据库版本管理</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%9E%E6%8E%A5%E6%B1%A0/" rel="tag">数据库连接池</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/blog/tags/%E7%BC%93%E5%AD%98/" rel="tag">缓存</a><span class="tag-list-count">2</span></li></ul></div></div><div class="widget"><h3 class="widget-title">标签云</h3><div class="widget-body tagcloud"><a href="/blog/tags/MyBatis/" style="font-size:13px">MyBatis</a> <a href="/blog/tags/Oauth2/" style="font-size:13px">Oauth2</a> <a href="/blog/tags/Spring-Security/" style="font-size:13px">Spring Security</a> <a href="/blog/tags/SpringMVC/" style="font-size:13px">SpringMVC</a> <a href="/blog/tags/docker/" style="font-size:13px">docker</a> <a href="/blog/tags/dubbo/" style="font-size:13px">dubbo</a> <a href="/blog/tags/git/" style="font-size:13px">git</a> <a href="/blog/tags/jdbctemplate/" style="font-size:13px">jdbctemplate</a> <a href="/blog/tags/jerkins/" style="font-size:13px">jerkins</a> <a href="/blog/tags/job/" style="font-size:14px">job</a> <a href="/blog/tags/jpa/" style="font-size:13px">jpa</a> <a href="/blog/tags/pdf/" style="font-size:13px">pdf</a> <a href="/blog/tags/redis/" style="font-size:13px">redis</a> <a href="/blog/tags/%E5%88%86%E5%BA%93%E5%88%86%E8%A1%A8/" style="font-size:13px">分库分表</a> <a href="/blog/tags/%E5%A4%9A%E6%95%B0%E6%8D%AE%E6%BA%90/" style="font-size:13px">多数据源</a> <a href="/blog/tags/%E6%95%B0%E6%8D%AE%E5%BA%93%E7%89%88%E6%9C%AC%E7%AE%A1%E7%90%86/" style="font-size:13px">数据库版本管理</a> <a href="/blog/tags/%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%9E%E6%8E%A5%E6%B1%A0/" style="font-size:13px">数据库连接池</a> <a href="/blog/tags/%E7%BC%93%E5%AD%98/" style="font-size:14px">缓存</a></div></div><div class="widget"><h3 class="widget-title">归档</h3><div class="widget-body"><ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/blog/archives/2020/02/">二月 2020</a><span class="archive-list-count">5</span></li><li class="archive-list-item"><a class="archive-list-link" href="/blog/archives/2020/01/">一月 2020</a><span class="archive-list-count">8</span></li><li class="archive-list-item"><a class="archive-list-link" href="/blog/archives/2019/12/">十二月 2019</a><span class="archive-list-count">4</span></li></ul></div></div><div class="widget"><h3 class="widget-title">最新文章</h3><div class="widget-body"><ul class="recent-post-list list-unstyled no-thumbnail"><li><div class="item-inner"><p class="item-category"><a class="category-link" href="/blog/categories/labs/">labs</a></p><p class="item-title"><a href="/blog/2020/02/18/labs-springboot2-async-Job/" class="title">Spring Boot 异步任务入门</a></p><p class="item-date"><time datetime="2020-02-18T05:10:39.000Z" itemprop="datePublished">2020-02-18</time></p></div></li><li><div class="item-inner"><p class="item-category"><a class="category-link" href="/blog/categories/labs/">labs</a></p><p class="item-title"><a href="/blog/2020/02/18/labs-springboot2-job/" class="title">Spring Boot 定时任务入门</a></p><p class="item-date"><time datetime="2020-02-18T02:26:31.000Z" itemprop="datePublished">2020-02-18</time></p></div></li><li><div class="item-inner"><p class="item-category"><a class="category-link" href="/blog/categories/labs/">labs</a></p><p class="item-title"><a href="/blog/2020/02/17/labs-springboot2-database-version-control/" class="title">Spring Boot 数据库版本管理入门</a></p><p class="item-date"><time datetime="2020-02-17T12:05:54.000Z" itemprop="datePublished">2020-02-17</time></p></div></li><li><div class="item-inner"><p class="item-category"><a class="category-link" href="/blog/categories/labs/">labs</a></p><p class="item-title"><a href="/blog/2020/02/17/labs-springboot2-sharding-datasource/" class="title">Spring Boot 分库分表入门</a></p><p class="item-date"><time datetime="2020-02-17T08:15:31.000Z" itemprop="datePublished">2020-02-17</time></p></div></li><li><div class="item-inner"><p class="item-category"><a class="category-link" href="/blog/categories/labs/">labs</a></p><p class="item-title"><a href="/blog/2020/02/13/labs-springboot2-dynamic-datasource/" class="title">Spring Boot 多数据源（读写分离）入门</a></p><p class="item-date"><time datetime="2020-02-13T03:18:37.000Z" itemprop="datePublished">2020-02-13</time></p></div></li></ul></div></div></div></aside><aside class="sidebar sidebar-toc collapse in" id="collapseToc" itemscope itemtype="http://schema.org/WPSideBar"><div class="slimContent"><nav id="toc" class="article-toc"><h3 class="toc-title">文章目录</h3><ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#1-概述"><span class="toc-text">1. 概述</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#2-快速入门"><span class="toc-text">2. 快速入门</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#2-1-引入依赖"><span class="toc-text">2.1 引入依赖</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-2-JpaApplication"><span class="toc-text">2.2 JpaApplication</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-3-配置文件"><span class="toc-text">2.3 配置文件</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-4-UserDO"><span class="toc-text">2.4 UserDO</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-5-UserRepository01"><span class="toc-text">2.5 UserRepository01</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-6-简单测试"><span class="toc-text">2.6 简单测试</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#3-分页操作"><span class="toc-text">3. 分页操作</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#3-1-UserRepository02"><span class="toc-text">3.1 UserRepository02</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-2-简单测试"><span class="toc-text">3.2 简单测试</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#4-基于方法名查询"><span class="toc-text">4. 基于方法名查询</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#4-1-UserRepository03"><span class="toc-text">4.1 UserRepository03</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#4-2-简单测试"><span class="toc-text">4.2 简单测试</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#5-基于注解查询"><span class="toc-text">5. 基于注解查询</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#5-1-UserRepository04"><span class="toc-text">5.1 UserRepository04</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#5-2-简单测试"><span class="toc-text">5.2 简单测试</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#666-彩蛋"><span class="toc-text">666. 彩蛋</span></a></li></ol></nav></div></aside><main class="main" role="main"><div class="content"><article id="post-labs-springboot2-jpa" class="article article-type-post" itemscope itemtype="http://schema.org/BlogPosting"><div class="article-header"><h1 class="article-title" itemprop="name">Spring Boot JPA 入门</h1><div class="article-meta"><span class="article-date"><i class="icon icon-calendar-check"></i> <a href="/blog/2020/01/08/labs-springboot2-jpa/" class="article-date"><time datetime="2020-01-08T09:57:01.000Z" itemprop="datePublished">2020-01-08</time></a></span> <span class="article-category"><i class="icon icon-folder"></i> <a class="article-category-link" href="/blog/categories/labs/">labs</a></span> <span class="article-tag"><i class="icon icon-tags"></i> <a class="article-tag-link" href="/blog/tags/jpa/" rel="tag">jpa</a></span> <span class="article-read hidden-xs"><i class="icon icon-eye-fill" aria-hidden="true"></i> <span id="busuanzi_container_page_pv"><span id="busuanzi_value_page_pv">0</span></span></span> <span class="post-comment"><i class="icon icon-comment"></i> <a href="/blog/2020/01/08/labs-springboot2-jpa/#comments" class="article-comment-link">评论</a></span> <span class="post-wordcount hidden-xs" itemprop="wordCount">字数统计: 3.7k(字)</span> <span class="post-readcount hidden-xs" itemprop="timeRequired">阅读时长: 16(分)</span></div></div><div class="article-entry marked-body" itemprop="articleBody"><blockquote><p>摘要: 原创出处 <a href="http://www.iocoder.cn/Spring-Boot/JPA/" target="_blank" rel="noopener">http://www.iocoder.cn/Spring-Boot/JPA/</a> 「芋道源码」，卡姆在此基础上略作修改，欢迎转载，保留摘要，谢谢！</p></blockquote><h1 id="1-概述"><a href="#1-概述" class="headerlink" title="1. 概述"></a>1. 概述</h1><p>我们，咱们来学习下 <a href="https://spring.io/projects/spring-data-jpa" target="_blank" rel="noopener">Spring Data JPA</a> 。</p><p>相信不少胖友之前有了解过 JPA、Hibernate ，那么 JPA、Hibernate、Spring Data JPA 这三者是什么关系呢？我们来一起理一理。</p><p><a href="https://zh.wikipedia.org/wiki/Java持久化API" target="_blank" rel="noopener"><strong>JPA</strong></a> ，全称 Java Persistence API ，是由 Java 定义的 Java ORM 以及实体操作 API 的标准。正如最早学习 JDBC 规范，Java 自身并未提供相关的实现，而是 MySQL 提供 MySQL <a href="https://mvnrepository.com/artifact/mysql/mysql-connector-java" target="_blank" rel="noopener">mysql-connector-java</a> 驱动，Oracle 提供 <a href="https://mvnrepository.com/artifact/oracle/oracle-jdbc" target="_blank" rel="noopener">oracle-jdbc</a> 驱动。而实现 JPA 规范的有：</p><ul><li><a href="https://hibernate.org/orm/" target="_blank" rel="noopener">Hibernate ORM</a></li><li><a href="https://www.oracle.com/middleware/technologies/top-link.html" target="_blank" rel="noopener">Oracle TopLink</a></li><li><a href="http://openjpa.apache.org/" target="_blank" rel="noopener">Apache OpenJPA</a></li></ul><p>Spring Data JPA ，是 <a href="https://spring.io/projects/spring-data" target="_blank" rel="noopener">Spring Data</a> 提供的一套<strong>简化</strong>的 JPA 开发的框架。</p><ul><li>内置 CRUD、分页、排序等功能的操作。</li><li>根据约定好的<a href="https://docs.spring.io/spring-data/jpa/docs/2.2.0.RELEASE/reference/html/#jpa.query-methods.query-creation" target="_blank" rel="noopener">方法名</a>规则，自动生成对应的查询操作。</li><li>使用 <code>@Query</code> 注解，自定义 SQL 。</li></ul><blockquote><p>FROM <a href="https://www.cnblogs.com/xiaoheike/p/5150553.html" target="_blank" rel="noopener">《spring data jpa hibernate jpa 三者之间的关系》</a></p><p><img src="1.png" alt="img"></p></blockquote><p>当然，绝大多数情况下，我们使用的 JPA 实现框架是 <a href="https://hibernate.org/orm/" target="_blank" rel="noopener">Hibernate ORM</a> 。所以整个调用过程是：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">应用程序 &#x3D;&gt; Repository &#x3D;&gt; Spring Data JPA &#x3D;&gt; Hibernate</span><br></pre></td></tr></table></figure><h1 id="2-快速入门"><a href="#2-快速入门" class="headerlink" title="2. 快速入门"></a>2. 快速入门</h1><blockquote><p>示例代码对应仓库：<a href="https://github.com/yuyong725/labs-springboot2/tree/master/labs-springboot2-data-access/labs-springboot2-jpa" target="_blank" rel="noopener">labs-springboot2-jpa</a> 。</p></blockquote><p>本小节，我们会使用 <a href="https://github.com/spring-projects/spring-boot/tree/master/spring-boot-project/spring-boot-starters/spring-boot-starter-data-jpa" target="_blank" rel="noopener">spring-boot-starter-data-jpa</a> 自动化配置 Spring Data JPA 。同时，演示 Spring Data JPA 的 CRUD 的操作。</p><h2 id="2-1-引入依赖"><a href="#2-1-引入依赖" class="headerlink" title="2.1 引入依赖"></a>2.1 引入依赖</h2><p>在 <code>pom.xml</code> 文件中，引入相关依赖。</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">project</span> <span class="attr">xmlns</span>=<span class="string">"http://maven.apache.org/POM/4.0.0"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xmlns:xsi</span>=<span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xsi:schemaLocation</span>=<span class="string">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>labs-springboot2-data-access<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>cn.javadog<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.0-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">modelVersion</span>&gt;</span>4.0.0<span class="tag">&lt;/<span class="name">modelVersion</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>labs-springboot2-jpa<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 本示例，我们使用 MySQL --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>mysql<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mysql-connector-java<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 实现对 Spring Data JPA 的自动化配置 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-data-jpa<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 方便等会写单元测试 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-test<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">scope</span>&gt;</span>test<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.projectlombok<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>lombok<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure><p>具体每个依赖的作用，胖友自己认真看下添加的所有注释噢。</p><p>另外，在 <code>spring-boot-starter-data-jpa</code> 中，已经默认引入了 <code>Hibernate</code> 和 <code>spring-boot-starter-jdbc</code> 的依赖。</p><h2 id="2-2-JpaApplication"><a href="#2-2-JpaApplication" class="headerlink" title="2.2 JpaApplication"></a>2.2 JpaApplication</h2><p>创建 <code>JpaApplication</code> 类，配置 <code>@SpringBootApplication</code> 注解即可。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// JpaApplication.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">JpaApplication</span> </span>&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="2-3-配置文件"><a href="#2-3-配置文件" class="headerlink" title="2.3 配置文件"></a>2.3 配置文件</h2><p>在 <code>application.yml</code> 中，添加 JPA 配置，如下：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="comment"># datasource 数据源配置内容</span></span><br><span class="line">  <span class="attr">datasource:</span></span><br><span class="line">    <span class="attr">url:</span> <span class="string">jdbc:mysql://127.0.0.1:3306/labs-springboot2?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai</span></span><br><span class="line">    <span class="attr">driver-class-name:</span> <span class="string">com.mysql.cj.jdbc.Driver</span></span><br><span class="line">    <span class="attr">username:</span> <span class="string">root</span></span><br><span class="line">    <span class="attr">password:</span> <span class="string">root</span></span><br><span class="line">  <span class="comment"># JPA 配置内容，对应 JpaProperties 类</span></span><br><span class="line">  <span class="attr">jpa:</span></span><br><span class="line">    <span class="attr">show-sql:</span> <span class="literal">true</span> <span class="comment"># 打印 SQL 。生产环境，建议关闭</span></span><br><span class="line">    <span class="comment"># Hibernate 配置内容，对应 HibernateProperties 类</span></span><br><span class="line">    <span class="attr">hibernate:</span></span><br><span class="line">      <span class="attr">ddl-auto:</span> <span class="string">none</span></span><br></pre></td></tr></table></figure><ul><li><p><code>datasource</code> 配置项，配置 datasource 数据源配置内容。</p></li><li><p><code>jpa</code> 配置项，配置 Spring Data JPA 配置内容，对应 <a href="https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaProperties.java" target="_blank" rel="noopener">JpaProperties</a> 类。</p></li><li><p><code>hibernate</code> 配置项，配置 Hibernate 配置内容，对应 <a href="https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateProperties.java" target="_blank" rel="noopener">HibernateProperties</a> 类。</p><ul><li><p>实际项目无需配置 <code>hibernate</code> 配置项，这里仅仅是演示，让胖友知道这回事。</p></li><li><blockquote><p>FROM <a href="https://blog.csdn.net/qq_36666651/article/details/80719259" target="_blank" rel="noopener">《jpa 的 hibernate.ddl-auto 的几个属性值区别》</a></p><ul><li>create ：每次加载 hibernate 时都会删除上一次的生成的表，然后根据你的 model 类再重新来生成新表，哪怕两次没有任何改变也要这样执行，这就是导致数据库表数据丢失的一个重要原因。</li><li>create-drop ：每次加载 hibernate 时根据 model 类生成表，但是 sessionFactory 一关闭，表就自动删除。</li><li>update ：最常用的属性，第一次加载 hibernate 时根据 model 类会自动建立起表的结构（前提是先建立好数据库），以后加载 hibernate 时根据 model 类自动更新表结构，即使表结构改变了但表中的行仍然存在不会删除以前的行。要注意的是当部署到服务器后，表结构是不会被马上建立起来的，是要等应用第一次运行起来后才会。</li><li>validate ：每次加载 hibernate 时，验证创建数据库表结构，只会和数据库中的表进行比较，不会创建新表，但是会插入新值。<ul><li>建议，生产环境下，建议配置 <code>none</code> ，不使用 Hibernate Auto DDL 功能。启动个项目，就自动变更数据库表结构，多危险啊~</li></ul></li></ul></blockquote></li></ul></li></ul><h2 id="2-4-UserDO"><a href="#2-4-UserDO" class="headerlink" title="2.4 UserDO"></a>2.4 UserDO</h2><p>创建 <code>UserDO.java</code> 类，用户 DO 。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// UserDO.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@Accessors</span>(chain = <span class="keyword">true</span>)</span><br><span class="line"><span class="meta">@Entity</span></span><br><span class="line"><span class="meta">@Table</span>(name = <span class="string">"jpa_users"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UserDO</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 用户编号</span></span><br><span class="line"><span class="comment">     * strategy 设置使用数据库主键自增策略；</span></span><br><span class="line"><span class="comment">     * generator 设置插入完成后，查询最后生成的 ID 填充到该属性中。</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Id</span></span><br><span class="line">    <span class="meta">@GeneratedValue</span>(strategy = GenerationType.IDENTITY, generator = <span class="string">"JDBC"</span>)</span><br><span class="line">    <span class="keyword">private</span> Integer id;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 账号</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Column</span>(nullable = <span class="keyword">false</span>)</span><br><span class="line">    <span class="keyword">private</span> String username;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 密码（明文）</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * ps：生产环境下，千万不要明文噢</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Column</span>(nullable = <span class="keyword">false</span>)</span><br><span class="line">    <span class="keyword">private</span> String password;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 创建时间</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Column</span>(name = <span class="string">"create_time"</span>, nullable = <span class="keyword">false</span>)</span><br><span class="line">    <span class="keyword">private</span> Date createTime;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>关于 JPA 的注解的详细说明，胖友后面再看看 <a href="https://www.jianshu.com/p/1b759ef26ff3" target="_blank" rel="noopener">《Spring Data JPA 中常用的注解详解》</a> 文章。我们，继续往下看。</p><p>对应的创建表的 SQL 如下：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> <span class="string">`jpa_users`</span> (</span><br><span class="line">  <span class="string">`id`</span> <span class="built_in">int</span>(<span class="number">11</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span> AUTO_INCREMENT <span class="keyword">COMMENT</span> <span class="string">'用户编号'</span>,</span><br><span class="line">  <span class="string">`username`</span> <span class="built_in">varchar</span>(<span class="number">64</span>) <span class="keyword">COLLATE</span> utf8mb4_bin <span class="keyword">DEFAULT</span> <span class="literal">NULL</span> <span class="keyword">COMMENT</span> <span class="string">'账号'</span>,</span><br><span class="line">  <span class="string">`password`</span> <span class="built_in">varchar</span>(<span class="number">32</span>) <span class="keyword">COLLATE</span> utf8mb4_bin <span class="keyword">DEFAULT</span> <span class="literal">NULL</span> <span class="keyword">COMMENT</span> <span class="string">'密码'</span>,</span><br><span class="line">  <span class="string">`create_time`</span> datetime <span class="keyword">DEFAULT</span> <span class="literal">NULL</span> <span class="keyword">COMMENT</span> <span class="string">'创建时间'</span>,</span><br><span class="line">  PRIMARY <span class="keyword">KEY</span> (<span class="string">`id`</span>),</span><br><span class="line">  <span class="keyword">UNIQUE</span> <span class="keyword">KEY</span> <span class="string">`idx_username`</span> (<span class="string">`username`</span>)</span><br><span class="line">) <span class="keyword">ENGINE</span>=<span class="keyword">InnoDB</span> <span class="keyword">DEFAULT</span> <span class="keyword">CHARSET</span>=utf8mb4 <span class="keyword">COLLATE</span>=utf8mb4_bin;</span><br></pre></td></tr></table></figure><h2 id="2-5-UserRepository01"><a href="#2-5-UserRepository01" class="headerlink" title="2.5 UserRepository01"></a>2.5 UserRepository01</h2><p>创建 <code>UserRepository01</code> 接口。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// UserRepository01.java</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">UserRepository01</span> <span class="keyword">extends</span> <span class="title">CrudRepository</span>&lt;<span class="title">UserDO</span>, <span class="title">Integer</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>继承 <code>org.springframework.data.repository.CrudRepository</code> 接口，第一个泛型设置对应的实体是 UserDO ，第二个泛型设置对应的主键类型是 Integer 。</li><li>因为实现了 CrudRepository 接口，Spring Data JPA 会自动生成对应的 CRUD 的代码。具体 CrudRepository 提供了哪些操作，胖友点击 <a href="https://github.com/spring-projects/spring-data-commons/blob/master/src/main/java/org/springframework/data/repository/CrudRepository.java" target="_blank" rel="noopener">CrudRepository</a> 查看。</li></ul><h2 id="2-6-简单测试"><a href="#2-6-简单测试" class="headerlink" title="2.6 简单测试"></a>2.6 简单测试</h2><p>创建 <code>UserRepository01Test</code> 测试类，我们来测试一下简单的 UserRepository01 的每个操作。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// UserRepository01.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@RunWith</span>(SpringRunner<span class="class">.<span class="keyword">class</span>)</span></span><br><span class="line"><span class="class">@<span class="title">SpringBootTest</span></span></span><br><span class="line"><span class="class"><span class="title">public</span> <span class="title">class</span> <span class="title">UserRepository01Test</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserRepository01 userRepository;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 插入一条记录</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testSave</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        UserDO user = <span class="keyword">new</span> UserDO().setUsername(UUID.randomUUID().toString())</span><br><span class="line">                .setPassword(<span class="string">"jpa-save"</span>).setCreateTime(<span class="keyword">new</span> Date());</span><br><span class="line">        userRepository.save(user);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 更新一条记录</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testUpdate</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 先查询一条记录</span></span><br><span class="line">        Optional&lt;UserDO&gt; userDO = userRepository.findById(<span class="number">1</span>);</span><br><span class="line">        Assert.isTrue(userDO.isPresent(), <span class="string">"记录不能为空"</span>);</span><br><span class="line">        <span class="comment">// 更新一条记录</span></span><br><span class="line">        UserDO updateUser = userDO.get();</span><br><span class="line">        updateUser.setPassword(<span class="string">"jpa-update"</span>).setUsername(<span class="string">"calm"</span>);</span><br><span class="line">        userRepository.save(updateUser);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据 ID 编号，删除一条记录</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testDelete</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        userRepository.deleteById(<span class="number">2</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据 ID 编号，查询一条记录</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testSelectById</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        Optional&lt;UserDO&gt; userDO = userRepository.findById(<span class="number">1</span>);</span><br><span class="line">        System.out.println(userDO.get());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据 ID 编号数组，查询多条记录</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testSelectByIds</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        Iterable&lt;UserDO&gt; users = userRepository.findAllById(Arrays.asList(<span class="number">1</span>, <span class="number">4</span>));</span><br><span class="line">        users.forEach(System.out::println);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>具体的，胖友可以自己跑跑，妥妥的。</p><ul><li>对<code>spring-boot-starter-test</code> 不熟悉的胖友，如果仔细阅读了卡姆springboot2系列其他的入门文章，可能会发现，这里<code>@SpringBootTest</code>为什么没有指定<code>classes = JpaApplication.class</code>，这是因为它会自动扫描<code>@SpringBootApplication</code> 注解加在了哪个类上面，如果没有多个启动类的话，是可以简化不写的。</li></ul><h1 id="3-分页操作"><a href="#3-分页操作" class="headerlink" title="3. 分页操作"></a>3. 分页操作</h1><p>Spring Data 提供 <a href="https://github.com/spring-projects/spring-data-commons/blob/master/src/main/java/org/springframework/data/repository/PagingAndSortingRepository.java" target="_blank" rel="noopener">PagingAndSortingRepository</a> 接口，继承 CrudRepository 接口，在 CRUD 操作的基础上，额外提供分页和排序的操作。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// PagingAndSortingRepository.java</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">PagingAndSortingRepository</span>&lt;<span class="title">T</span>, <span class="title">ID</span>&gt; <span class="keyword">extends</span> <span class="title">CrudRepository</span>&lt;<span class="title">T</span>, <span class="title">ID</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="function">Iterable&lt;T&gt; <span class="title">findAll</span><span class="params">(Sort sort)</span></span>; <span class="comment">// 排序操作</span></span><br><span class="line"></span><br><span class="line">	<span class="function">Page&lt;T&gt; <span class="title">findAll</span><span class="params">(Pageable pageable)</span></span>; <span class="comment">// 分页操作</span></span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="3-1-UserRepository02"><a href="#3-1-UserRepository02" class="headerlink" title="3.1 UserRepository02"></a>3.1 UserRepository02</h2><p>创建 <code>UserRepository02</code> 接口。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// UserRepository02.java</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">UserRepository02</span> <span class="keyword">extends</span> <span class="title">PagingAndSortingRepository</span>&lt;<span class="title">UserDO</span>, <span class="title">Integer</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>实现 PagingAndSortingRepository 接口，第一个泛型设置对应的实体是 UserDO ，第二个泛型设置对应的主键类型是 Integer 。</li></ul><h2 id="3-2-简单测试"><a href="#3-2-简单测试" class="headerlink" title="3.2 简单测试"></a>3.2 简单测试</h2><p>创建 <code>UserRepository02Test</code> 测试类，我们来测试一下简单的 UserRepository02 的每个操作。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// UserRepository02Test.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@RunWith</span>(SpringRunner<span class="class">.<span class="keyword">class</span>)</span></span><br><span class="line"><span class="class">@<span class="title">SpringBootTest</span></span></span><br><span class="line"><span class="class"><span class="title">public</span> <span class="title">class</span> <span class="title">UserRepository02Test</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserRepository02 userRepository;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 排序</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testFindAll</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 创建排序条件</span></span><br><span class="line">        Sort sort = <span class="keyword">new</span> Sort(Sort.Direction.DESC, <span class="string">"id"</span>);</span><br><span class="line">        <span class="comment">// 执行排序操作</span></span><br><span class="line">        Iterable&lt;UserDO&gt; iterable = userRepository.findAll(sort);</span><br><span class="line">        <span class="comment">// 打印</span></span><br><span class="line">        iterable.forEach(System.out::println);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 分页</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testFindPage</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 创建排序条件</span></span><br><span class="line">        Sort sort = <span class="keyword">new</span> Sort(Sort.Direction.DESC, <span class="string">"id"</span>);</span><br><span class="line">        <span class="comment">// 创建分页条件，page从0开始</span></span><br><span class="line">        Pageable pageable = PageRequest.of(<span class="number">1</span>, <span class="number">5</span>, sort);</span><br><span class="line">        <span class="comment">// 执行分页操作</span></span><br><span class="line">        Page&lt;UserDO&gt; page = userRepository.findAll(pageable);</span><br><span class="line">        <span class="comment">// 打印</span></span><br><span class="line">        System.out.println(page.getTotalElements());</span><br><span class="line">        System.out.println(page.getTotalPages());</span><br><span class="line">        System.out.println(page.getContent().size());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>具体的，胖友可以自己跑跑，妥妥的。</p><h1 id="4-基于方法名查询"><a href="#4-基于方法名查询" class="headerlink" title="4. 基于方法名查询"></a>4. 基于方法名查询</h1><p>在 Spring Data 中，支持根据方法名作生成对应的查询（<code>WHERE</code>）条件，进一步简化我们使用 JPA ，卡姆认为这是Jpa相比MyBatis最大的优势，虽然我还是习惯MyBatis 🐶。使用方法是方法名以 <code>findBy</code>、<code>existsBy</code>、<code>countBy</code>、<code>deleteBy</code> 开头，后面跟具体的条件。具体的规则，在 <a href="https://docs.spring.io/spring-data/jpa/docs/2.2.0.RELEASE/reference/html/#jpa.query-methods.query-creation" target="_blank" rel="noopener">《Spring Data JPA —— Query Creation》</a> 文档中，已经详细提供。如下：</p><table><thead><tr><th align="center">关键字</th><th align="center">方法示例</th><th align="center">JPQL snippet</th></tr></thead><tbody><tr><td align="center">And</td><td align="center">findByLastnameAndFirstname</td><td align="center">… where x.lastname = ?1 and x.firstname = ?2</td></tr><tr><td align="center">Or</td><td align="center">findByLastnameOrFirstname</td><td align="center">… where x.lastname = ?1 or x.firstname = ?2</td></tr><tr><td align="center">Is, Equals</td><td align="center">findByFirstname,findByFirstnameIs,findByFirstnameEquals</td><td align="center">… where x.firstname = ?1</td></tr><tr><td align="center">Between</td><td align="center">findByStartDateBetween</td><td align="center">… where x.startDate between ?1 and ?2</td></tr><tr><td align="center">LessThan</td><td align="center">findByAgeLessThan</td><td align="center">… where x.age &lt; ?1</td></tr><tr><td align="center">LessThanEqual</td><td align="center">findByAgeLessThanEqual</td><td align="center">… where x.age &lt;= ?1</td></tr><tr><td align="center">GreaterThan</td><td align="center">findByAgeGreaterThan</td><td align="center">… where x.age &gt; ?1</td></tr><tr><td align="center">GreaterThanEqual</td><td align="center">findByAgeGreaterThanEqual</td><td align="center">… where x.age &gt;= ?1</td></tr><tr><td align="center">After</td><td align="center">findByStartDateAfter</td><td align="center">… where x.startDate &gt; ?1</td></tr><tr><td align="center">Before</td><td align="center">findByStartDateBefore</td><td align="center">… where x.startDate &lt; ?1</td></tr><tr><td align="center">IsNull, Null</td><td align="center">findByAge(Is)Null</td><td align="center">… where x.age is null</td></tr><tr><td align="center">IsNotNull, NotNull</td><td align="center">findByAge(Is)NotNull</td><td align="center">… where x.age not null</td></tr><tr><td align="center">Like</td><td align="center">findByFirstnameLike</td><td align="center">… where x.firstname like ?1</td></tr><tr><td align="center">NotLike</td><td align="center">findByFirstnameNotLike</td><td align="center">… where x.firstname not like ?1</td></tr><tr><td align="center">StartingWith</td><td align="center">findByFirstnameStartingWith</td><td align="center">… where x.firstname like ?1 (parameter bound with appended %)</td></tr><tr><td align="center">EndingWith</td><td align="center">findByFirstnameEndingWith</td><td align="center">… where x.firstname like ?1 (parameter bound with prepended %)</td></tr><tr><td align="center">Containing</td><td align="center">findByFirstnameContaining</td><td align="center">… where x.firstname like ?1 (parameter bound wrapped in %)</td></tr><tr><td align="center">OrderBy</td><td align="center">findByAgeOrderByLastnameDesc</td><td align="center">… where x.age = ?1 order by x.lastname desc</td></tr><tr><td align="center">Not</td><td align="center">findByLastnameNot</td><td align="center">… where x.lastname &lt;&gt; ?1</td></tr><tr><td align="center">In</td><td align="center">findByAgeIn(Collection ages)</td><td align="center">… where x.age in ?1</td></tr><tr><td align="center">NotIn</td><td align="center">findByAgeNotIn(Collection ages)</td><td align="center">… where x.age not in ?1</td></tr><tr><td align="center">True</td><td align="center">findByActiveTrue()</td><td align="center">… where x.active = true</td></tr><tr><td align="center">False</td><td align="center">findByActiveFalse()</td><td align="center">… where x.active = false</td></tr><tr><td align="center">IgnoreCase</td><td align="center">findByFirstnameIgnoreCase</td><td align="center">… where UPPER(x.firstame) = UPPER(?1)</td></tr></tbody></table><ul><li>注意，如果我们有排序需求，可以使用 <code>OrderBy</code> 关键字。</li></ul><blockquote><p>IDEA 牛逼，提供的插件已经能够自动提示上述关键字。太强了~</p></blockquote><h2 id="4-1-UserRepository03"><a href="#4-1-UserRepository03" class="headerlink" title="4.1 UserRepository03"></a>4.1 UserRepository03</h2><p>创建 <code>UserRepository03</code> 接口。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// UserRepository03.java</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">UserRepository03</span> <span class="keyword">extends</span> <span class="title">PagingAndSortingRepository</span>&lt;<span class="title">UserDO</span>, <span class="title">Integer</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function">UserDO <span class="title">findByUsername</span><span class="params">(String username)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function">Page&lt;UserDO&gt; <span class="title">findByCreateTimeAfter</span><span class="params">(Date createTime, Pageable pageable)</span></span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>对于分页操作，需要使用到 Pageable 参数，需要作为方法的最后一个参数。</li></ul><h2 id="4-2-简单测试"><a href="#4-2-简单测试" class="headerlink" title="4.2 简单测试"></a>4.2 简单测试</h2><p>创建 <code>UserRepository03Test</code> 测试类，我们来测试一下简单的 UserRepository03 的每个操作。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// UserRepository03.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@RunWith</span>(SpringRunner<span class="class">.<span class="keyword">class</span>)</span></span><br><span class="line"><span class="class">@<span class="title">SpringBootTest</span></span></span><br><span class="line"><span class="class"><span class="title">public</span> <span class="title">class</span> <span class="title">UserRepository03Test</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserRepository03 userRepository;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testFindByUsername</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        UserDO user = userRepository.findByUsername(<span class="string">"calm"</span>);</span><br><span class="line">        System.out.println(user);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testFindByCreateTimeAfter</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 创建分页条件</span></span><br><span class="line">        Pageable pageable = PageRequest.of(<span class="number">1</span>, <span class="number">5</span>);</span><br><span class="line">        <span class="comment">// 临时 Demo ，实际不建议这么写</span></span><br><span class="line">        Date createTime = <span class="keyword">new</span> Date(<span class="number">2020</span> - <span class="number">1900</span>, Calendar.JANUARY, <span class="number">8</span>);</span><br><span class="line">        <span class="comment">// 执行分页操作</span></span><br><span class="line">        Page&lt;UserDO&gt; page = userRepository.findByCreateTimeAfter(createTime, pageable);</span><br><span class="line">        <span class="comment">// 打印</span></span><br><span class="line">        System.out.println(page.getTotalElements());</span><br><span class="line">        System.out.println(page.getTotalPages());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>具体的，胖友可以自己跑跑，妥妥的。</p><h1 id="5-基于注解查询"><a href="#5-基于注解查询" class="headerlink" title="5. 基于注解查询"></a>5. 基于注解查询</h1><p>虽然 Spring Data JPA 提供了非常强大的功能，可以满足绝大多数业务场景下的 CRUD 操作，但是可能部分情况下，我们可以使用在方法上添加 <a href="https://docs.spring.io/spring-data/jpa/docs/current/api/org/springframework/data/jpa/repository/Query.html" target="_blank" rel="noopener">@Query</a> 注解，实现自定义的 SQL 操作。</p><p>如果是更新或删除的 SQL 操作，需要<strong>额外</strong>在方法上添加 <a href="https://docs.spring.io/spring-data/jpa/docs/current/api/org/springframework/data/jpa/repository/Modifying.html" target="_blank" rel="noopener">@Modifying</a> 注解。</p><p>下面，我们来编写一个简单的示例。</p><h2 id="5-1-UserRepository04"><a href="#5-1-UserRepository04" class="headerlink" title="5.1 UserRepository04"></a>5.1 UserRepository04</h2><p>创建 <code>UserRepository04</code> 接口。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// UserRepository04.java</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">UserRepository04</span> <span class="keyword">extends</span> <span class="title">PagingAndSortingRepository</span>&lt;<span class="title">UserDO</span>, <span class="title">Integer</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Query</span>(<span class="string">"SELECT u FROM UserDO u WHERE u.username = ?1"</span>)</span><br><span class="line">    <span class="function">UserDO <span class="title">findByUsername01</span><span class="params">(String username)</span></span>; <span class="comment">// &lt;1&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Query</span>(<span class="string">"SELECT u FROM UserDO u WHERE u.username = :username"</span>)</span><br><span class="line">    <span class="function">UserDO <span class="title">findByUsername02</span><span class="params">(@Param(<span class="string">"username"</span>)</span> String username)</span>; <span class="comment">// &lt;2&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Query</span>(value = <span class="string">"SELECT * FROM jpa_users u WHERE u.username = :username"</span>, nativeQuery = <span class="keyword">true</span>)</span><br><span class="line">    <span class="function">UserDO <span class="title">findByUsername03</span><span class="params">(@Param(<span class="string">"username"</span>)</span> String username)</span>; <span class="comment">// &lt;3&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Modifying</span></span><br><span class="line">    <span class="meta">@Query</span>(<span class="string">"UPDATE UserDO  u SET u.username = :username WHERE u.id = :id"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">updateUsernameById</span><span class="params">(Integer id, String username)</span></span>; <span class="comment">// &lt;4&gt;</span></span><br><span class="line">  </span><br><span class="line">  	 <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 分页</span></span><br><span class="line"><span class="comment">     * value 属性，编写查询分页列表的 SQL</span></span><br><span class="line"><span class="comment">     * countQuery 属性，编写记录总数的 SQL</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Query</span>(value = <span class="string">"SELECT * FROM jpa_users WHERE create_time &gt; ?1"</span>,</span><br><span class="line">        countQuery = <span class="string">"SELECT count(*) FROM jpa_users WHERE create_time &gt; ?1"</span>,</span><br><span class="line">        nativeQuery = <span class="keyword">true</span>)</span><br><span class="line">    <span class="function">Page&lt;UserDO&gt; <span class="title">findAfterCreateTime</span><span class="params">(Date createTime, Pageable pageable)</span></span>; <span class="comment">// &lt;5&gt;</span></span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p><code>&lt;1&gt;</code> 处，使用 <code>@Query</code> 自定义了一个 SQL 操作，并且参数使用<strong>占位符(<code>?</code>)</strong> + <strong>参数位置</strong>的形式。</p></li><li><p><code>&lt;2&gt;</code> 处，和 <code>&lt;1&gt;</code> 类似，差异在于使用<strong>占位符(<code>:</code>)</strong> + <strong>参数名字(需要使用 <code>@Param</code> 声明)</strong>的形式。</p></li><li><p><code>&lt;3&gt;</code>处，和 <code>&lt;2&gt;</code> 类似，差别在于我们增加了 <code>nativeQuery = true</code>，表示在 <code>@Query</code> 自定义的是</p><p>原生 SQL，而非在 <code>&lt;1&gt;</code> 和 <code>&lt;2&gt;</code> 自定义的是JPQL。进一步的说：</p><ul><li><code>&lt;1&gt;</code> 和 <code>&lt;2&gt;</code> 处，<code>FROM UserDO</code> ，使用的是实体名。</li><li><code>&lt;3&gt;</code> 处，使用的是表名。</li><li>对 JPQL 不是很了解的胖友，可以看看 <a href="https://www.jianshu.com/p/4a4410075bab" target="_blank" rel="noopener">《JPQL 的学习》</a> 文章。</li></ul></li><li><p><code>&lt;4&gt;</code> 处，定义了更新操作，需要加上 <code>@Modifying</code> 注解。 另外，我们发可以现，使用参数名时，可以不用配合 <code>@Param</code> 注解。</p></li><li><p><code>&lt;5&gt;</code> 处，对于分页操作，需要在 <code>@Query</code> 编写查询分页列表和记录总数两条 SQL</p></li></ul><h2 id="5-2-简单测试"><a href="#5-2-简单测试" class="headerlink" title="5.2 简单测试"></a>5.2 简单测试</h2><p>创建 <code>UserRepository04Test</code> 测试类，我们来测试一下简单的 UserRepository04 的每个操作。代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// UserRepository04Test.java</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@RunWith</span>(SpringRunner<span class="class">.<span class="keyword">class</span>)</span></span><br><span class="line"><span class="class">@<span class="title">SpringBootTest</span></span></span><br><span class="line"><span class="class"><span class="title">public</span> <span class="title">class</span> <span class="title">UserRepository04Test</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserRepository04 userRepository;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testFindIdByUsername01</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        UserDO user = userRepository.findByUsername01(<span class="string">"calm"</span>);</span><br><span class="line">        System.out.println(user);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testFindIdByUsername02</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        UserDO user = userRepository.findByUsername02(<span class="string">"calm"</span>);</span><br><span class="line">        System.out.println(user);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testFindIdByUsername03</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        UserDO user = userRepository.findByUsername03(<span class="string">"calm"</span>);</span><br><span class="line">        System.out.println(user);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 更新操作，需要在事务中，不加<span class="doctag">@Transactional</span>会直接GG</span></span><br><span class="line"><span class="comment">     * 在单元测试中，事务默认回滚，所以胖友可能怎么测试，事务都不更新。</span></span><br><span class="line"><span class="comment">     * 可以添加 <span class="doctag">@Rollback</span>(value = false) 指定不回滚，这会导致 <span class="doctag">@Transactional</span> 不生效，即使报错也不回滚</span></span><br><span class="line"><span class="comment">     * 也就是说，回滚/不回滚完全取决于 <span class="doctag">@Rollback</span> 的值，但你又必须加上 <span class="doctag">@Transactional</span>，有趣吧，哈哈</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="meta">@Transactional</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testUpdateUsernameById</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        userRepository.updateUsernameById(<span class="number">5</span>, <span class="string">"calm-update3"</span>);</span><br><span class="line">        <span class="comment">// throw new RuntimeException("指定@Rollback(value = false)，测试报错回滚");</span></span><br><span class="line">    &#125;</span><br><span class="line">  </span><br><span class="line">  	<span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testFindAfterCreateTime</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 创建分页条件</span></span><br><span class="line">        Pageable pageable = PageRequest.of(<span class="number">1</span>, <span class="number">5</span>);</span><br><span class="line">        <span class="comment">// 临时 Demo ，实际不建议这么写</span></span><br><span class="line">        Date createTime = <span class="keyword">new</span> Date(<span class="number">2020</span> - <span class="number">1900</span>, Calendar.JANUARY, <span class="number">8</span>);</span><br><span class="line">        <span class="comment">// 执行分页操作</span></span><br><span class="line">        Page&lt;UserDO&gt; page = userRepository.findAfterCreateTime(createTime, pageable);</span><br><span class="line">        <span class="comment">// 打印</span></span><br><span class="line">        System.out.println(page.getTotalElements());</span><br><span class="line">        System.out.println(page.getTotalPages());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>具体的，胖友可以自己跑跑，妥妥的。</p><h1 id="666-彩蛋"><a href="#666-彩蛋" class="headerlink" title="666. 彩蛋"></a>666. 彩蛋</h1><p>本文仅仅是 Spring Data JPA 的简单入门，还有部分内容，胖友可以自己在去学习下：</p><ul><li><p><a href="https://docs.spring.io/spring-data/jpa/docs/2.2.0.RELEASE/reference/html/#jpa.query-methods.named-queries" target="_blank" rel="noopener">《Using JPA Named Queries》</a> ，可以使用 XML 自定义 SQL 操作。</p></li><li><p><a href="https://my.oschina.net/weechang93/blog/1576594" target="_blank" rel="noopener">《Spring Data JPA 实现逻辑删除》</a> ，绝大多数业务场景下，我们不会使用 <code>DELETE</code> 物理删除，而是通过标志位进行逻辑删除。</p></li><li><p>多表查询</p><ul><li><p>方式一：<a href="https://blog.wuwii.com/jpa-query-muti.html" target="_blank" rel="noopener">《JPA 多表查询的解决办法》</a></p></li><li><p>方式二：<a href="https://blog.csdn.net/moshowgame/article/details/80058270" target="_blank" rel="noopener">《JPA 多表关联查询》</a></p></li><li><blockquote><p>如果在这种情况下，Repository 需要继承 JpaRepository 接口。</p></blockquote></li></ul></li></ul><p>如果胖友想找一个完整的，使用 JPA 的项目，可以看看 <a href="https://github.com/ctripcorp/apollo" target="_blank" rel="noopener">Apollo</a> 。它是携程开源的配置中心，目前最好用的配置中心，基本没有之一，嘿嘿。</p><p>推荐阅读：</p><ul><li><a href="http://www.iocoder.cn/Performance-Testing/MySQL-benchmark/?self" target="_blank" rel="noopener">《性能测试 —— MySQL 基准测试》</a></li><li><a href="http://www.iocoder.cn/Spring-Boot/dynamic-datasource/?self" target="_blank" rel="noopener">《芋道 Spring Boot 多数据源（读写分离）入门》</a> 对应 <a href="https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-17" target="_blank" rel="noopener">lab-17</a> 。</li><li><a href="http://www.iocoder.cn/Spring-Boot/sharding-datasource/?self" target="_blank" rel="noopener">《芋道 Spring Boot 分库分表入门》</a> 对应 <a href="https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-18" target="_blank" rel="noopener">lab-18</a> 。</li></ul></div><div class="article-footer"><blockquote class="mt-2x"><ul class="post-copyright list-unstyled"><li class="post-copyright-link hidden-xs"><strong>本文链接：</strong> <a href="https://calm_java.gitee.io/blog/2020/01/08/labs-springboot2-jpa/" title="Spring Boot JPA 入门" target="_blank" rel="external">https://calm_java.gitee.io/blog/2020/01/08/labs-springboot2-jpa/</a></li><li class="post-copyright-license"><strong>版权声明： </strong>本博客所有文章除特别声明外，均采用 <a href="http://creativecommons.org/licenses/by/4.0/deed.zh" target="_blank" rel="external">CC BY 4.0 CN协议</a> 许可协议。转载请注明出处！</li></ul></blockquote><div class="panel panel-default panel-badger"><div class="panel-body"><figure class="media"><div class="media-left"><a href="https://github.com/yuyong725" target="_blank" class="img-burn thumb-sm visible-lg"><img src="/blog/images/avatar.jpg" class="img-rounded w-full" alt=""></a></div><div class="media-body"><h3 class="media-heading"><a href="https://github.com/yuyong725" target="_blank"><span class="text-dark">卡姆</span><small class="ml-1x">a Java coder need calm</small></a></h3><div>享受编程</div></div></figure></div></div></div></article><section id="comments"></section></div><nav class="bar bar-footer clearfix" data-stick-bottom><div class="bar-inner"><ul class="pager pull-left"><li class="prev"><a href="/blog/2020/01/09/labs-springboot2-cache/" title="Spring Boot 缓存 Cache 入门"><i class="icon icon-angle-left" aria-hidden="true"></i><span>&nbsp;&nbsp;上一篇</span></a></li><li class="next"><a href="/blog/2020/01/08/labs-springboot2-mybatis/" title="Spring Boot MyBatis 入门"><span>下一篇&nbsp;&nbsp;</span><i class="icon icon-angle-right" aria-hidden="true"></i></a></li><li class="toggle-toc"><a class="toggle-btn" data-toggle="collapse" href="#collapseToc" aria-expanded="true" title="文章目录" role="button"><span>[&nbsp;</span><span>文章目录</span> <i class="text-collapsed icon icon-anchor"></i> <i class="text-in icon icon-close"></i> <span>]</span></a></li></ul><button type="button" class="btn btn-fancy btn-donate pop-onhover bg-gradient-warning" data-toggle="modal" data-target="#donateModal"><span>赏</span></button><div class="bar-right"><div class="share-component" data-sites="wechat" data-mobile-sites="wechat"></div></div></div></nav><div class="modal modal-center modal-small modal-xs-full fade" id="donateModal" tabindex="-1" role="dialog"><div class="modal-dialog" role="document"><div class="modal-content donate"><button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button><div class="modal-body"><div class="donate-box"><div class="donate-head"><p>感谢您的支持，我会继续努力的!</p></div><div class="tab-content"><div role="tabpanel" class="tab-pane fade active in" id="alipay"><div class="donate-payimg"><img src="/blog/images/donate/alipayimg.jpeg" alt="扫码支持" title="扫一扫"></div><p class="text-muted mv">扫码打赏，你说多少就多少</p><p class="text-grey">打开支付宝扫一扫，即可进行扫码打赏哦</p></div><div role="tabpanel" class="tab-pane fade" id="wechatpay"><div class="donate-payimg"><img src="/blog/images/donate/wechatpayimg.jpeg" alt="扫码支持" title="扫一扫"></div><p class="text-muted mv">扫码打赏，你说多少就多少</p><p class="text-grey">打开微信扫一扫，即可进行扫码打赏哦</p></div></div><div class="donate-footer"><ul class="nav nav-tabs nav-justified" role="tablist"><li role="presentation" class="active"><a href="#alipay" id="alipay-tab" role="tab" data-toggle="tab" aria-controls="alipay" aria-expanded="true"><i class="icon icon-alipay"></i> 支付宝</a></li><li role="presentation"><a href="#wechatpay" role="tab" id="wechatpay-tab" data-toggle="tab" aria-controls="wechatpay" aria-expanded="false"><i class="icon icon-wepay"></i> 微信支付</a></li></ul></div></div></div></div></div></div></main><footer class="footer" itemscope itemtype="http://schema.org/WPFooter"></footer><script src="//cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"></script><script>window.jQuery||document.write('<script src="js/jquery.min.js"><\/script>')</script><script src="/blog/js/plugin.min.js"></script><script src="/blog/js/application.js"></script><script>!function(T){var N={TRANSLATION:{POSTS:"文章",PAGES:"页面",CATEGORIES:"分类",TAGS:"标签",UNTITLED:"(未命名)"},ROOT_URL:"/blog/",CONTENT_URL:"/blog/content.json"};T.INSIGHT_CONFIG=N}(window)</script><script src="/blog/js/insight.js"></script><script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script><script src="//cdn.jsdelivr.net/npm/gitalk@1.4.0/dist/gitalk.min.js"></script><script src="//cdn.jsdelivr.net/npm/blueimp-md5@2.10.0/js/md5.min.js"></script><script type="text/javascript">var gitalk=new Gitalk({clientID:"adf0c8bfdd8796029917",clientSecret:"a88c2e2e83e2b180f62ad927dcb347871dfe9f4b",repo:"myblog-comment",owner:"yuyong725",admin:["yuyong725"],id:md5(location.pathname),distractionFreeMode:!0});gitalk.render("comments")</script></body></html><!-- rebuild by neat -->